package main

import (
	"JoDocker/cgroups"
	"JoDocker/cgroups/subsystems"
	"JoDocker/container"
	"JoDocker/network"
	"JoDocker/util"
	"encoding/json"
	"fmt"
	"github.com/Sirupsen/logrus"
	"os"
	"strconv"
	"strings"
)

func Run(tty bool, cmdSlc []string, volume string, resConfigPtr *subsystems.ResourceConfig, containerName string, imageName string, envSlc []string, networkName string, portMapping []string) {
	containerId := util.RandId(10)
	if containerName == "" {
		containerName = containerId
	}

	parentCmdPtr, writePipe := container.NewParentProcess(tty, containerName, volume, imageName, envSlc)
	if parentCmdPtr == nil {
		logrus.Errorf("New parent process error")
		return
	}
	// start()完毕,代表子进程已经启动了.
	if err := parentCmdPtr.Start(); err != nil {
		logrus.Error(err)
	}

	// 记录容器信息
	containerName, err := recordContainerInfo(parentCmdPtr.Process.Pid, cmdSlc, containerName, containerId, volume)
	if err != nil {
		logrus.Errorf("Record container info error %v", err)
		return
	}

	// 使用jodocker-cgroup作为cgroup name
	cGroupManagePtr := cgroups.MakeCGroupManager("jodocker-cgroup")

	defer cGroupManagePtr.Destory()

	// 设置内存,CPU的限制
	cGroupManagePtr.Set(resConfigPtr)
	// 当前进程写入tasks中, 生效限制
	cGroupManagePtr.Apply(parentCmdPtr.Process.Pid)

	if networkName != "" {
		// 配置容器网络
		network.Init()
		containerInfo := &container.ContainerInfo{
			Id:          containerId,
			Pid:         strconv.Itoa(parentCmdPtr.Process.Pid),
			Name:        containerName,
			PortMapping: portMapping,
		}
		// 容器链接网络
		if err := network.Connect(networkName, containerInfo); err != nil {
			logrus.Errorf("Error Connect Network %v", err)
			return
		}
	}

	// 命令写入管道中发送给子进程
	sendInitCommand(cmdSlc, writePipe)

	if tty {
		parentCmdPtr.Wait()

		// 交互模式下的删除容器信息
		deleteContainerInfo(containerName)

		// 删除容器的挂载
		container.DeleteWorkSpace(containerName, volume)

		//os.Exit(0)
	}
}

// 通过写管道发送命令
func sendInitCommand(cmdSlc []string, writePipe *os.File) {
	command := strings.Join(cmdSlc, " ")
	logrus.Infof("run => command all is %s", command)
	writePipe.WriteString(command)
	writePipe.Close()
}

// 记录容器信息
func recordContainerInfo(containerPid int, commandSlc []string, containerName string, containerId string, volume string) (string, error) {
	command := strings.Join(commandSlc, "")
	containerInfoPtr := container.MakeContainerInfo(containerId, strconv.Itoa(containerPid), command, containerName, volume)

	jsonBytes, err := json.Marshal(containerInfoPtr)
	if err != nil {
		logrus.Errorf("Record container info err %v", err)
		return "", err
	}
	jsonStr := string(jsonBytes)
	// 容器信息路径
	dirPath := fmt.Sprintf(container.DefaultInfoLocation, containerName)
	if err := os.MkdirAll(dirPath, 0622); err != nil {
		logrus.Errorf("Mkdir error %s error %v", dirPath, err)
		return "", err
	}
	filePath := dirPath + "/" + container.ConfigFileName
	file, err := os.Create(filePath)
	defer file.Close()
	if err != nil {
		logrus.Errorf("Create file %s error %v", filePath, err)
		return "", err
	}
	if _, err := file.WriteString(jsonStr); err != nil {
		logrus.Errorf("file write string error %v", err)
		return "", err
	}
	return containerName, nil
}

// 删除容器信息
func deleteContainerInfo(containerName string) {
	dirPath := fmt.Sprintf(container.DefaultInfoLocation, containerName)
	if err := os.RemoveAll(dirPath); err != nil {
		logrus.Errorf("Remove dir %s error %v", dirPath, err)
	}
}
